/*
 * Estacionamiento.cpp
 *
 *  Created on: 04/10/2012
 */

#include "Estacionamiento.h"

Estacionamiento::Estacionamiento(int cantidadLugares, int costoHora):
	cantidadAutos(MemoriaCompartida<int>()),
	cantidadLugares (cantidadLugares),
	costoHora (costoHora),
	lugares (vector<MemoriaCompartida<Lugar> >()),
	semaforos(vector<Semaforo>()),
	semaforoCuentas((char*) ARCHIVO_CUENTAS_SEMAFORO, 1, 'a'){

}

void Estacionamiento::inicializar(){
	for (int i = 0; i < cantidadLugares; i++){
		MemoriaCompartida<Lugar> lugar;
		int estado1 = lugar.crear ((char*) ARCHIVO_LUGARES_MEM_COMP, (char) i);
		
		if (estado1 == SHM_OK){
			Semaforo semaforo((char*)ARCHIVO_LUGARES_SEMAFOROS, 1, (char) i);
			this->lugares.push_back(lugar);
			this->semaforos.push_back(semaforo);
		} else {
			Log::getInstance()->loguear("Estacionamiento: Error al crear la memoria para el lugar " + intToString(i) + ".");
		}
	}

	int cero = 0;
	int estado2 = this->cantidadAutos.crear ((char*) ARCHIVO_CANTIDAD_AUTOS_MEM_COMP, 'a');
	if (estado2 == SHM_OK){
		Log::getInstance()->loguear("Estacionamiento: Memoria cantidad autos creada exitosamente.");
		this->cantidadAutos.escribir(cero);
	} else {
		Log::getInstance()->loguear("Estacionamiento: Error al crear la memoria para la cantidad de autos.");
	}
	
	int estado3 = this->montoFacturado.crear ((char*) ARCHIVO_MONTO_FACTURADO_MEM_COMP, 'b');
	if (estado3 == SHM_OK){
		Log::getInstance()->loguear("Estacionamiento: Memoria monto facturado creada exitosamente.");
		this->montoFacturado.escribir(cero);
	} else {
		Log::getInstance()->loguear("Estacionamiento: Error al crear la memoria para el monto facturado.");
	}

	int estado4 = this->totalAutos.crear ((char*) ARCHIVO_TOTAL_AUTOS_MEM_COMP, 'c');
	if (estado4 == SHM_OK){
		Log::getInstance()->loguear("Estacionamiento: Memoria total de autos creada exitosamente.");
		this->totalAutos.escribir(cero);
	} else {
		Log::getInstance()->loguear("Estacionamiento: Error al crear la memoria para el total de autos.");
	}
	
	Log::getInstance()->loguear("Estacionamiento: inicializado.");
}

Estacionamiento::~Estacionamiento() {
	// Se liberan las memorias compartidas
	vector<MemoriaCompartida<Lugar> >::iterator itMemorias= this->lugares.begin();
	while(itMemorias!=this->lugares.end()){
		itMemorias->liberar();
		itMemorias++;
	}

	this->cantidadAutos.liberar();
	this->montoFacturado.liberar();
	this->totalAutos.liberar();
}

void Estacionamiento::liberarRecursos(){
	Log::getInstance()->loguear("Estacionamiento: elimino los semáforos de los lugares.");

	for (vector<Semaforo>::iterator itSemaforos = this->semaforos.begin(); itSemaforos != this->semaforos.end(); itSemaforos++){
		itSemaforos->eliminar();
	}
	semaforoCuentas.eliminar();
}

/**
 * Consulta si un lugar está ocupado e intenta reservarlo.
 *
 * Devuelve false si el lugar no estaba ocupado y lo pudo reservar.
 */
bool Estacionamiento::consultarLugar(int posicion){
	semaforos.at(posicion).p();

	Lugar lugar = lugares.at(posicion).leer();
	bool ocupado = lugar.isOcupado();
	if(!ocupado){
		lugar.setOcupado(true);
		lugares.at(posicion).escribir(lugar);

		Log::getInstance()->loguear("Estacionamiento: Consulto el lugar "
			+ intToString(posicion) + " y está desocupado. Lo reservo.");
	} else {
		Log::getInstance()->loguear("Estacionamiento: Consulto el lugar "
			+ intToString(posicion) + " y está ocupado.");
	}

	semaforos.at(posicion).v();
	
	return ocupado;
}

int Estacionamiento::getCantidadLugares(){
	return cantidadLugares;
}

/**
 * Busca y reserva un lugar disponible en el estacionamiento.
 * En caso de no encontrarlo devuelve un -1.
 */
int Estacionamiento::buscarReservarLugarDisponible(){
	int i = 0;
	while(i < this->cantidadLugares){
		if (!this->consultarLugar(i)){
			return i;
		}
		i++;
	};
	return -1;
}

void Estacionamiento::liberarLugar(Ticket ticket){
	int numeroLugar = ticket.getLugarOcupado();
	semaforos.at(numeroLugar).p();

	Lugar lugar = lugares.at(numeroLugar).leer();
	lugar.setOcupado(false);
	this->disminuirAutos(ticket.getHorasEstacionado());
	
	lugares.at(numeroLugar).escribir(lugar);
	Log::getInstance()->loguear("Estacionamiento: Libero el lugar " + intToString(numeroLugar) + ".");

	semaforos.at(numeroLugar).v();
}

int Estacionamiento::consultarCantidadAutos(){
	int cantidad = this->cantidadAutos.leer();
	return cantidad;
}

int Estacionamiento::consultarMontoFacturado(){
	int monto = this->montoFacturado.leer();
	return monto;
}

int Estacionamiento::consultarTotalAutos(){
	int total = this->totalAutos.leer();
	return total;
}

void Estacionamiento::aumentarAutos(){
	semaforoCuentas.p();
	
	int cantidad = this->cantidadAutos.leer();
	cantidad++;
	this->cantidadAutos.escribir(cantidad);
	
	int total = this->totalAutos.leer();
	total++;
	this->totalAutos.escribir(total);

	semaforoCuentas.v();
}

void Estacionamiento::disminuirAutos(int horas){
	semaforoCuentas.p();

	int cantidad = this->cantidadAutos.leer();
	cantidad--;
	this->cantidadAutos.escribir(cantidad);
	
	int monto = this->montoFacturado.leer();
	monto += horas * this->costoHora;
	this->montoFacturado.escribir(monto);

	semaforoCuentas.v();
}

void Estacionamiento::imprimirEstadisticas(){
	Log::getInstance()->imprimir("Estacionamiento: La cantidad total de autos que estacionaron fue " + 
			intToString(this->consultarTotalAutos()) + ".");
	Log::getInstance()->imprimir("Estacionamiento: El monto total facturado es $" +
			intToString(this->consultarMontoFacturado()) + ".");
}
